<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
	<title>GTK+, Glade, and Internationalization</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
	<meta name="description" content="A step-by-step showing how to use GTK+ to load Glade files, how to create Glade files, and how to internationalize glade files.">
	<link rel="stylesheet" href="GTK+,%20Glade,%20and%20Internationalization_files/servin.css" type="text/css">
</head>
<body>

<p><strong><a href="http://servin.com/">Servin Mobile Software</a></strong></p>

<hr>
<h1>GTK+, Glade, and Internationalization</h1>

<hr>
<p><a href="http://servin.com/contact.php?fn=contact">Norman McEntire</a></p>
<p>Version 1.3 January 13</p>
<p>Copyright © 2009 Servin Corporation. <a href="http://servin.com/">http://servin.com</a></p>

<hr>
<h2>Introduction</h2>

<p>
GTK+ and Glade are often used to create GUI interfaces on GNU/Linux systems.
</p>
<p>
Here is a short step-by-step showing how to use GTK+ to load Glade files,
how to create Glade files,
and how to internationalize glade files.
</p>

<hr>
<h2>What You Will Learn</h2>

<ul>
<li>How to use glade_xml_new() to load Glade files.</li>
<li>How to use use Glade.</li>
<li>How to create <em>po</em> directory, and contents of that directory.</li>
<li>How to use <strong>xgettext</strong> command.</li>
<li>How to use <strong>msgfmt</strong> command.</li>
<li>How to use <strong>setlocale(), bindtextdomain(), textdomain()</strong> functions.</li>
<li>How to use the <strong>LC_ALL</strong> environment variable.</li>
</ul>

<hr>
<h2>Prerequisites</h2>

<ul>
<li>
Your development environment is already setup to compile GTK+ code.
</li>
<li>
You already have Glade installed.
</li>
</ul>

<hr>
<h2>What We Will Translate</h2>
<p>
Our goal is to write a GUI app that supports
English, French, and Spanish:
</p>
<pre>English: Hello
French: Salut
Spanish: Hola
</pre>

<hr>
<h2>glade_xml_new()</h2>

<p>
In this exercise,
you will create <strong>demo.c</strong> so that it loads a file
created with Glade.
</p>

<ol>
<li>
Use your editor to create a file named <strong>demo.c</strong> as shown below.
You can also <a href="http://www.servin.com/linux/demo.c">download</a> the file.
<pre>#include &lt;gtk/gtk.h&gt;
#include &lt;glade/glade.h&gt;

int main(int argc, char *argv[])
{
        gtk_init(&amp;argc, &amp;argv);

        GladeXML *xml = glade_xml_new("demo.glade", NULL, NULL);

        if (xml == NULL) {
                g_print("glade_xml_new error\n");
		return 1;
	}

        gtk_main();

        return 0;
}
</pre>
</li>
<li>
Compile the program:
<pre># gcc -Wall -o demo demo.c $(pkg-config --cflags --libs gtk+-2.0 libglade-2.0)
</pre>
</li>
<li>
Run the program. For now do not worry about the error:
<pre>$ ./demo
(demo:15470): libglade-WARNING **: could not find glade file 'demo.glade'
glade_xml_new error
</pre>
</li>
</ol>

<hr>
<h2>Tour of Glade</h2>
<p>
Here you will tour Glade as it comes on Fedora 9.
</p>

<ol>

<li>
Use the top-level menu to startup Glade:
<pre>Top-Level Menu: Applications &gt; Programming &gt; Glade Interface Designer
</pre>
</li>

<li>
Depending on whether you have used Glade previously,
you will see one or more Windows open up.
At a minimum you will see a Window titled <strong>Glade</strong>.
You may see other windows, too, but ignore those for now.
</li>

<li>
On the Glade window,
select the <strong>About</strong> menu selection:
<pre>Glade Menu: Help &gt; About
</pre>
</li>

<li>
Observe the version of Glade.
In my case, the version number is 2.12.2.
</li>

<li>
There is a more recent version of glade, Glade-3,
that has additional features such a undo and redo,
but for our purposes Glade 2 will do fine.
</li>

<li>
On the Glade main menu, click on the <strong>Project</strong> menu:
<pre>- Project
	- New
	- Open
	- Build
	- Options
	- Quit
</pre>
</li>

<li>
We will cover these in more detail later.
For now, just make a quick note of the names of the menu items under Project.
</li>

<li>
On the Glade main menu, click on the <strong>Edit</strong> menu,
again just observing the options for now:
<pre>- Edit
	- Cut
	- Copy
	- Paste
	- Delete
</pre>
</li>

<li>
On the Glade main menu, click on the <strong>View</strong> menu.
In this case, enable the checkbox for each menu item:
<pre>- View
	- Show Palette
	- Show Property Editor
	- Show Widget Tree
	- Show Clipboard
</pre>
</li>

<li>
To repeat, enable the checkbox for each menu item under the <strong>View</strong> menu,
for the next several steps will introduce each of the windows.
</li>

<li>
Observe the window labeled <strong>Palette</strong>.
This window contains the list of widgets that you can add to the layout.
More on this in a moment.
</li>

<li>
Observe the window labeled <strong>Properties</strong>.
This window allows you to change the properties for the currently selected widget.
</li>

<li>
Observe the window labeled <strong>Widget Tree</strong>.
This window shows the widgets in the design, in a hierarchical manner.
</li>

</ol>

<hr>
<h2>Using Glade</h2>

<p>
Here we will use Glade to create a simple "Hello Glade" based GTK+ application.
We will have a main window, and a single label in the window.
We will not worry about GTK+ signals for this exercise.
</p>

<ol>

<li>
If you have not already done so,
startup Glade.
</li>

<li>
In the window titled <strong>Glade</strong>:
<pre>- Click on the toolbar icon titled New
- Select New GTK+ Project in answer to the New Project question
</pre>
</li>

<li>
In the window titled <strong>Palette</strong>:
<pre>- Make sure the GTK+ Basic button is pushed in (enabled)
- Click on the icon labeled Window
- Observe that window1 appears on your screen
- Observe that window1 appears in the Glade window
- Observe that window1 appears in the Widget Tree window
</pre>
</li>

<li>
In the window titled <strong>Properties: window1</strong>:
<pre>- Change the Title to say "Hello Glade"
- As you type the title, observe the instant change in the window
- Click on Default Width, and set to 300
- Click on Default Height, and set to 200
</pre>
</li>

<li>
In the window titled <strong>Palette</strong>:
<pre>- Click on the icon labeled "Label"
- Move your mouse over the "Hello Glade" window
- Observe the mouse pointer has a + symbol
- Click on the "Hello Glade" window, observing the label filling the window
- Observe the Widget Tree window shows window1 and label1 (when expanded)
</pre>
</li>

<li>
In the window titled <strong>Properties: label1</strong>:
<pre>- Change the Label text to say "How are you?"
</pre>
</li>

<li>
On the Glade toolbar, click on <strong>Save</strong>.
</li>

<li>
Observe the <string>Project Options dialog,
but do not yet click OK.
</string></li>

<li>
From a previous step,
we created the <strong>demo.c</strong> code to load <strong>demo.glade</strong>.
</li>

<li>
Use the <strong>Project Options</strong> dialog to save the file as <strong>demo.glade</strong>
in the same directory as the C source code.
</li>

<li>
Run the program,
and you should see the Window and label appear.
</li>

<li>
Press Ctrl+c to exit the program.
</li>

</ol>

<hr>
<h2>po Directory, xgettext, and Message Files</h2>

<p>
In this exercise,
you will create a <strong>po</strong> directory,
which will contain your message files.
</p>

<p>
You will also use the <strong>xgettext</strong> command to extract
strings from <strong>demo.glade</strong> into a message file.
The message files can then be edited by translators.
</p>

<ol>

<li>
Use the <strong>grep</strong> command to search for the word
<strong>translatable</strong> in the <strong>demo.glade</strong> file:
<pre>$ grep translatable demo.glade
  &lt;property name="title" translatable="yes"&gt;Hello Glade
      &lt;property name="label" translatable="yes"&gt;How Are You
</pre>
</li>

<li>
The <strong>translatable</strong> attribute can be used by the
<strong>xgettext</strong> tool to extract strings that need translation.
</li>

<li>
Create a directory named <strong>po</strong> at the top-level of your project directory:
<pre>$ mkdir po
</pre>
</li>

<li>
The <strong>po</strong> directory will contain your message files in
human readable form, one for each language you wish to support.
</li>

<li>
As a review, here is how your directory should look for this demo:
<pre>./demo.c
./demo.glade
./po
</pre>
</li>

<li>
Use the <strong>xgettext</strong> command to extract the translatable strings
from the <strong>demo.glade</strong> file:
<pre>$ xgettext --sort-output --keyword=translatable -o po/demo.pot demo.glade
</pre>
</li>

<li>
The resulting output file,
<strong>demo.pot</strong>,
is the <strong>template file</strong> that is used by translators
to translate the messages.
</li>

<li>
Use the <strong>cat</strong> command to display the contents of <strong>po/demo.pot</strong>:
<pre>$ cat po/demo.pot 
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR &lt;EMAIL@ADDRESS&gt;, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-01-13 06:05-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME &lt;EMAIL@ADDRESS&gt;\n"
"Language-Team: LANGUAGE &lt;LL@li.org&gt;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: demo.glade:8
msgid "Hello Glade"
msgstr ""

#: demo.glade:27
msgid "How are you?"
msgstr ""
</pre>
</li>

<li>
Edit the <strong>po/demo.pot</strong> file to set the charset as follows:
<pre>"Content-Type: text/plain; charset=UTF-8\n"
</pre>
</li>



<li>
Send the resulting <strong>demo.pot</strong> template file to your translators,
telling them to translate the massages,
and send back a file named with the <strong>two-letter language code</strong>,
and the <strong>.po</strong> file extension. For example:
<pre>fr.po - the French version of messages
es.po - the Spanish version of messages
</pre>
</li>

<li>
For this example, create the French version of the file:
<pre>$ cd po
$ cp demo.pot fr.po
$ vim fr.po
$ cat fr.po
...
#: demo.glade:8
msgid "Hello Glade"
msgstr "Salut Glade"

#: demo.glade:27
msgid "How are  you?"
msgstr "Comment alley-vous?"
</pre>
</li>

<li>
For this example, create the Spanish version of the file:
<pre>$ cp demo.pot es.po
$ vim es.po
$ cat es.po
...
#: demo.glade:8
msgid "Hello Glade"
msgstr "Hola"

#: demo.glade:27
msgid "How are  you?"
msgstr "Como Estas?"
</pre>
</li>

</ol>

<hr>
<h2>msgfmt Command</h2>

<p>
In this exercise,
you will use the <strong>msgfmt</strong> command to convert the
human readable <strong>.po</strong> files into a binary message format
file with the <strong>.mo</strong> file extension.
</p>

<p>
The resulting <strong>.mo</strong> files are placed
in the <strong>/usr/share/locale</strong> directory,
each language code going into a separate directory.
</p>

<ol>
<li>
Use the <strong>find</strong> and <strong>grep</strong> commands to tour
French language message files under <strong>/usr/share/locale</strong>:
<pre>$ find /usr/share/locale | grep fr
/usr/share/locale/fr/LC_MESSAGES
/usr/share/locale/fr/LC_MESSAGES/alacarte.mo
/usr/share/locale/fr/LC_MESSAGES/gtk20.mo
/usr/share/locale/fr/LC_MESSAGES/cracklib.mo
/usr/share/locale/fr/LC_MESSAGES/libexif-12.mo
/usr/share/locale/fr/LC_MESSAGES/binutils.mo
...
</pre>
</li>

<li>
In your <strong>po</strong> directory,
use the <strong>msgfmt</strong> command to
convert the <strong>.po</strong> files into binary message files
with a <strong>.mo</strong> file extension:
<pre>$ msgfmt fr.po -o fr.mo
$ msgfmt es.po -o es.mo
</pre>
</li>

<li>
Use the <strong>file</strong> command to confirm
that you have created binary message files:
<pre>$ file *mo
es.mo: GNU message catalog (little endian), revision 0, 2 messages
fr.mo: GNU message catalog (little endian), revision 0, 2 messages
</pre>
</li>

<li>
Copy the files to the proper location on your system,
making sure you call them <strong>demo.mo</strong>:
<pre>$ sudo cp fr.mo /usr/share/locale/fr/LC_MESSAGES/demo.mo
$ sudo cp es.mo /usr/share/locale/es/LC_MESSAGES/demo.mo
</pre>
</li>

</ol>

<hr>
<h2>setlocale(), bindtextdomain(), textdomain()</h2>

<p>
In this exercise,
you will add several functions to setpu translation:
</p><pre>- setlocale() - use setlocale(LC_ALL, "") to make program portable to all locales
- bindtextdomain(PACKAGE, LOCALDIR))
- textdomain(PACKAGE)
</pre>
<p></p>

<ol>

<li>
Enhance the <strong>demo.c</strong> as show below in red:
<pre>// demo-glade.c

#include &lt;gtk/gtk.h&gt;
#include &lt;glade/glade.h&gt;
<font color="red">
#include &lt;locale.h&gt;
#include &lt;libintl.h&gt;
</font>
int main(int argc, char *argv[])
{
<font color="red">
        // Set the current local to default
        setlocale(LC_ALL, "");

	// bindtextdomain(DOMAINNAME, DIRNAME)
	//
	// Specify that the DOMAINNAME message catalog
	// will be found in DIRNAME rather than in 
	// the system locale data base.
        bindtextdomain("demo", "/usr/share/locale");

	// testdomain(DOMAINNAME)
	//
	// Set the current default message catalog to DOMAINNAME.
        textdomain("demo");
</font>
        gtk_init(&amp;argc, &amp;argv);

        GladeXML *xml = glade_xml_new("demo.glade", NULL, NULL);

        if (xml == NULL) {
                g_print("glade_xml_new error\n");
                return 1;
        }

        gtk_main();

        return 0;
}
</pre>
</li>

<li>
Rebuild the application:
<pre>$ gcc -Wall -o demo $(pkg-config --cflags --libs gtk+-2.0 libglade-2.0) demo.c
</pre>
</li>

<li>
Run the application using the <strong>strace</strong> command
to capture all the <strong>open()</strong> calls
the <strong>demo</strong> program makes while running:
<pre>$ strace -o asdf.txt -eopen ./demo
</pre>
</li>

<li>
Filter the output of the <strong>asdf.txt</strong> file to only show
access to files that are named <strong>demo</strong>:
<pre>$ grep demo asdf.txt 
open("demo.glade", O_RDONLY)            = 4
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/demo.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/demo.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/demo.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/demo.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
</pre>
Observe the program going to <strong>/usr/sahre/locale/en/LC_MESSAGES/demo.mo</strong>,
but it does not find that messages file.
</li>

<li>
Run the command again,
this time using the <strong>LC_ALL</strong> environment variable:
<pre>$ LC_ALL="fr_FR" strace -o asdf.txt -e open ./demo
</pre>
</li>

<li>
Again filter the output:
<pre>$ grep demo asdf.txt 
open("demo.glade", O_RDONLY)            = 4
open("/usr/share/locale/fr_FR/LC_MESSAGES/demo.mo", O_RDONLY) = 5
open("/usr/share/locale/fr/LC_MESSAGES/demo.mo", O_RDONLY) = 5
</pre>
Observe the access to <strong>/usr/share/locale/fr/LC_MESSAGES/demo.mo</strong>.
</li>

<li>
Run the application using the default language:
<pre>$ ./demo
</pre>
</li>

<li>
Run the application using the French language:
<pre>$ LC_ALL="fr_FR" ./demo
</pre>
</li>

</ol>


<hr>
<h2>Skills Review</h2>
<ul>

<li>Glade</li>
<li>glade_xml_new()</li>
<li>po directory</li>
<li>xgettext command</li>
<li>msgfmt command</li>
<li>setlocale()</li>
<li>bindtextdomain()</li>
<li>textdomain()</li>
<li>LC_ALL</li>
</ul>

<hr>
<h2>How To Contact Author</h2>
<p>
Feel free to <a href="http://servin.com/contact.php?fn=contact">contact the author</a>
for any of the following:
</p>
<ul>
<li>You have a question or comment about this mini-course.</li>
<li>You need to hire Servin to help with your software development project.</li>
<li>You need to hire Servin to give an on-site training course for your software development team.</li>
</ul>

<hr>
<p>
<small>
Updated 2009 Jan 13<br>
Content viewable on all web browsers, including smart mobile phone devices.<br>
Copyright © 1995-2009 Servin Corporation. All rights reserved.
</small>
</p>




</body></html>